-
Notifications
You must be signed in to change notification settings - Fork 851
Refactor build pipeline for performance and dev output reliability #886
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor build pipeline for performance and dev output reliability #886
Conversation
📝 WalkthroughWalkthroughAdds CI npm and Webpack filesystem caching; refactors build.mjs into a variant-aware, optionally parallel build system with per-variant filesystem cache, esbuild-based minification, optional thread-loader, improved asset copying/zipping, sourceBuildDir support, and updates docs and dependencies. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Dev as Developer / CI
participant Build as build.mjs
participant WP as Webpack Compiler
participant FS as Filesystem
participant Arch as Archiver/Zip
Note over Build,WP: Per-variant orchestration, caching & esbuild minification
Dev->>Build: invoke build (env flags, sourceBuildDir)
activate Build
Build->>Build: compute variants, dirKey, variantId, cache keys
alt Parallel builds
par Variant A
Build->>WP: runWebpack(variant A, sourceBuildDir)
WP-->>FS: emit assets to per-variant dir (uses filesystem cache)
Build->>FS: copyFiles & finishOutput(variant A)
Build->>Arch: zipFolder(variant A)
and Variant B
Build->>WP: runWebpack(variant B, sourceBuildDir)
WP-->>FS: emit assets to per-variant dir
Build->>FS: copyFiles & finishOutput(variant B)
Build->>Arch: zipFolder(variant B)
end
else Sequential builds
loop each variant
Build->>WP: runWebpack(variant, sourceBuildDir)
WP-->>FS: emit assets
Build->>FS: finishOutput
Build->>Arch: zipFolder
end
end
Build-->>Dev: exit status (0 / non-zero on fatal)
deactivate Build
sequenceDiagram
autonumber
actor CI as GitHub Actions
participant Setup as actions/setup-node
participant CacheNpm as npm-cache (setup-node)
participant CacheWP as actions/cache
participant Job as build job
CI->>Setup: setup-node (cache: npm)
Setup-->>CacheNpm: restore npm cache (OS + lockfile)
CI->>Job: export NODE_VERSION
CI->>CacheWP: restore .cache/webpack using OS + NODE_VERSION + lockfile/build hash
CI->>Job: npm ci && npm run build
Job-->>CacheWP: save updated Webpack filesystem cache
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (2)
🧰 Additional context used🧠 Learnings (3)📓 Common learnings📚 Learning: 2025-09-14T18:01:17.933ZApplied to files:
📚 Learning: 2025-09-06T17:46:04.304ZApplied to files:
🔇 Additional comments (12)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Summary of Changes
Hello @PeterDaveHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request comprehensively refactors the build pipeline to enhance performance, configurability, and developer experience. By leveraging modern tools and optimizing existing processes, it aims to drastically reduce build times for both local development and production environments, leading to a more efficient and responsive workflow.
Highlights
- Build Tooling Upgrade: Replaced JavaScript and CSS minifiers with esbuild for significantly faster build times, improving both development and production workflows.
- Parallel Processing & Threading: Enabled thread-loader by default for Babel with dynamic worker allocation based on CPU cores, and introduced parallel production variant builds, all configurable via new environment variables.
- Optimized Caching: Implemented configurable Webpack filesystem cache compression, defaulting to no compression for faster warm builds on CPU-bound machines, enhancing build efficiency.
- SASS/CSS Enhancements: Adopted sass-embedded for SASS processing and utilized style-loader for faster CSS/SCSS compilation in development, while ensuring production outputs remain unchanged.
- Development Experience Improvements: Ensured 'watch-once' development builds exit cleanly and added placeholder CSS files to prevent 404 errors and reduce noise during development.
- CI/CD Efficiency: Integrated dependency caching into GitHub Actions workflows to accelerate continuous integration and deployment runs.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request significantly refactors the build pipeline for performance, introducing esbuild for minification, thread-loader for parallelism, and sass-embedded for SASS compilation. The changes are well-structured and bring substantial performance improvements. My review focuses on improving maintainability by reducing code duplication in the build script. I've identified two areas where helper functions could be introduced to make the code cleaner and more reusable: one for creating placeholder CSS files and another for handling the webpack build process.
PR Code Suggestions ✨Latest suggestions up to 5663e3c
Previous suggestions✅ Suggestions up to commit 6c89d29
✅ Suggestions up to commit abfc14d
✅ Suggestions up to commit b5f27c3
✅ Suggestions up to commit 0fdeae8
✅ Suggestions up to commit d9cc0eb
✅ Suggestions up to commit 7bad746
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Codex Review: Here are some suggestions.
Reply with @codex fix comments to fix any unresolved comments.
About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you open a pull request for review, mark a draft as ready, or comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex fix this CI failure" or "@codex address that feedback".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
package.json (1)
64-99: Add@babel/runtimeto package dependencies
Babel’s@babel/plugin-transform-runtimeis enabled in build.mjs (line 180) and listed under devDependencies, but@babel/runtimeis missing—this will cause runtime “module not found” errors.--- package.json +++ package.json @@ "dependencies": { + "@babel/runtime": "^7.24.7", }build.mjs (1)
173-193: @babel/plugin-transform-runtime is placed inside presets; it won’t runThe plugin configured under presets is ignored. Move it to plugins (and, if you keep it, ensure @babel/runtime is added to package.json dependencies).
options: { cacheDirectory: true, cacheCompression: false, - presets: [ - '@babel/preset-env', - { - plugins: ['@babel/plugin-transform-runtime'], - }, - ], - plugins: [ + presets: ['@babel/preset-env'], + plugins: [ + ['@babel/plugin-transform-runtime'], [ '@babel/plugin-transform-react-jsx', { runtime: 'automatic', importSource: 'preact', }, ], ], },Follow-up (package.json):
- Add "@babel/runtime" to dependencies if you keep the plugin, or remove the plugin here to avoid introducing a runtime dep.
🧹 Nitpick comments (3)
package.json (1)
91-97: sass + sass-embedded together: consider dropping onebuild.mjs uses sass-embedded as the loader implementation. Keeping both sass and sass-embedded increases install size; unless another tool requires sass, consider removing sass to slim CI installs. If you do need both, no action needed.
.github/workflows/pre-release-build.yml (1)
35-43: Broaden the webpack cache key to include build.mjsIncluding build.mjs in the cache key avoids restoring a cache that webpack will immediately invalidate due to buildDependencies. This saves time and bandwidth.
Apply:
- key: ${{ runner.os }}-webpack-${{ hashFiles('**/package-lock.json') }} + key: ${{ runner.os }}-webpack-${{ hashFiles('**/package-lock.json', 'build.mjs') }} - restore-keys: | - ${{ runner.os }}-webpack- + restore-keys: | + ${{ runner.os }}-webpack-.github/copilot-instructions.md (1)
33-35: Tiny grammar/polishMake the performance defaults sentence crisper.
-Performance defaults: esbuild is used for JS/CSS minification; dev injects CSS via style-loader, -prod extracts CSS via MiniCssExtractPlugin; thread-loader is enabled by default in both dev and prod. +Performance defaults: esbuild handles JS/CSS minification; in development CSS is injected via style-loader, +in production CSS is extracted via MiniCssExtractPlugin; thread-loader is enabled by default in both dev and prod.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (4)
.github/copilot-instructions.md(1 hunks).github/workflows/pre-release-build.yml(1 hunks)build.mjs(12 hunks)package.json(2 hunks)
🧰 Additional context used
🪛 LanguageTool
.github/copilot-instructions.md
[grammar] ~33-~33: There might be a mistake here.
Context: ...ation; dev injects CSS via style-loader, prod extracts CSS via MiniCssExtractPlug...
(QB_NEW_EN)
🔇 Additional comments (8)
package.json (1)
77-79: Verify esbuild-loader 4.3.0 compatibility with webpack ^5.92.1 and Node 20
package.json lists:
• webpack ^5.92.1
• esbuild ^0.25.9
• esbuild-loader ^4.3.0
Ensure your CI (running Node v20.x) installs these exact versions and completes a full webpack build without errors (for example,npm ci && npm run build), or runnpm ls webpack esbuild-loaderto confirm no version conflicts..github/workflows/pre-release-build.yml (1)
27-34: NPM cache step: LGTMPositioning before npm ci and scoping to ~/.npm is correct for faster restores without locking node_modules.
.github/copilot-instructions.md (1)
20-32: Docs match implementationThe documented BUILD_PARALLEL, BUILD_THREAD/BUILD_THREAD_WORKERS, BUILD_CACHE_COMPRESSION, and BUILD_WATCH_ONCE flags align with build.mjs behavior. Nice.
build.mjs (5)
92-101: Switch to esbuild for JS/CSS minification: LGTMConfiguration and targets look sane; legalComments: 'none' keeps bundles lean, and CSS uses esbuildMinify via css-minimizer.
160-171: Threading defaults and poolTimeout handling are appropriateEnabling thread-loader by default with CPU-derived workers and using poolTimeout = 0 for BUILD_WATCH_ONCE is a good balance for dev/prod.
364-375: Dev CSS placeholders are a nice touchAvoiding 404 noise when using style-loader in development improves DX without affecting production.
Also applies to: 385-395
237-246: Font asset handling: confirm fallback behaviorwoff2 is inlined, while woff/ttf are marked emit: false. That’s fine for modern Chromium/Firefox, but verify there’s no runtime path that requests woff/ttf (e.g., older Firefox ESR or custom user agents). If needed, either remove emit:false or conditionally emit only for production.
Suggested tweak if you need to emit in prod:
- { - test: /\.(woff|ttf)$/, - type: 'asset/resource', - generator: { - emit: false, - }, - }, + { + test: /\.(woff|ttf)$/, + type: 'asset/resource', + generator: { emit: !isProduction ? false : true }, + },
82-90: Filesystem cache compression control: LGTMEnv-driven compression with a safe default (disabled) aligns with the perf objective and warm-build wins.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Codex Review: Here are some suggestions.
Reply with @codex fix comments to fix any unresolved comments.
About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you open a pull request for review, mark a draft as ready, or comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex fix this CI failure" or "@codex address that feedback".
| output: { | ||
| filename: '[name].js', | ||
| path: path.resolve(__dirname, outdir), | ||
| path: path.resolve(__dirname, sourceBuildDir || outdir), | ||
| }, | ||
| mode: isProduction ? 'production' : 'development', | ||
| devtool: isProduction ? false : 'inline-source-map', | ||
| devtool: isProduction ? false : 'cheap-module-source-map', | ||
| cache: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P2] Copy emitted source maps into dev build output
Development builds switched devtool to cheap-module-source-map, which emits external *.js.map files. However the packaging step only copies the JS and CSS assets into build/chromium*/build/firefox* and never copies the generated source map files. When loading the unpacked extension in a browser the bundles will reference content-script.js.map, popup.js.map, etc., but those files are missing, so DevTools reports 404s and source maps are unusable. Either copy the .map files alongside the bundles or keep inline source maps to avoid the broken references.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
build.mjs (4)
35-56: Thread worker calculation has proper bounds checking.The logic for determining thread workers is robust with proper validation and capping to CPU count. Good defensive programming.
57-58: Consider making the production pool timeout configurable.The hardcoded 2000ms timeout could be made configurable for different environments.
-const PRODUCTION_POOL_TIMEOUT_MS = 2000 +const PRODUCTION_POOL_TIMEOUT_MS = process.env.BUILD_POOL_TIMEOUT ? + parseInt(process.env.BUILD_POOL_TIMEOUT, 10) : 2000
372-385: Add logging for skipped files in development.When files are skipped (especially CSS placeholders), it would be helpful to log this for debugging purposes.
if (!isProduction && isCss) return + console.log(`[build] Skipping missing CSS file: ${entryPoint.src} (placeholder will be created)`)
456-508: Parallel build implementation with temporary directories is well-structured.The parallel and sequential build paths are properly implemented with appropriate cleanup of temporary directories. The error handling ensures failed builds are properly reported.
🧹 Nitpick comments (4)
package.json (1)
92-94: Consider pinning sass-embedded to match sass version.While both
sassandsass-embeddedare at^1.91.0, having separate version specs could lead to divergence. Consider using a single shared version constraint or removing the redundantsasspackage ifsass-embeddedis the primary implementation.If
sass-embeddedis the primary Sass implementation being used inbuild.mjs, you could remove the regularsasspackage:- "sass": "^1.91.0", "sass-embedded": "^1.91.0",.github/workflows/pre-release-build.yml (1)
29-35: Consider adding npm cache version to the cache key.The npm cache key only includes the OS and package-lock hash. If npm itself is upgraded, the cache structure might change. Consider including npm version in the key.
- key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + key: ${{ runner.os }}-npm-v${{ steps.npm-version.outputs.version }}-${{ hashFiles('**/package-lock.json') }}You would need to add a step to capture npm version:
- name: Get npm version id: npm-version run: echo "version=$(npm -v)" >> $GITHUB_OUTPUT.github/copilot-instructions.md (1)
33-34: Fix minor grammar issue in the documentation.There's a minor grammar issue flagged by static analysis.
-Performance defaults: esbuild handles JS/CSS minification; in development CSS is injected via style-loader, -in production CSS is extracted via MiniCssExtractPlugin; thread-loader is enabled by default in both dev and prod. +Performance defaults: esbuild handles JS/CSS minification; in development CSS is injected via style-loader; +in production CSS is extracted via MiniCssExtractPlugin; thread-loader is enabled by default in both dev and prod.build.mjs (1)
20-34: Consider extracting cache compression validation to a helper function.The cache compression validation logic is quite verbose and could be simplified.
-const cacheCompressionEnv = process.env.BUILD_CACHE_COMPRESSION -let cacheCompressionOption -if (cacheCompressionEnv != null) { - const v = String(cacheCompressionEnv).trim().toLowerCase() - if (v === '' || v === '0' || v === 'false' || v === 'none') { - cacheCompressionOption = false - } else if (v === 'gzip' || v === 'brotli') { - cacheCompressionOption = v - } else { - console.warn( - `[build] Unknown BUILD_CACHE_COMPRESSION="${cacheCompressionEnv}", defaulting to no compression`, - ) - cacheCompressionOption = false - } -} +function parseCacheCompression(env) { + if (env == null) return undefined + const v = String(env).trim().toLowerCase() + if (['', '0', 'false', 'none'].includes(v)) return false + if (['gzip', 'brotli'].includes(v)) return v + console.warn(`[build] Unknown BUILD_CACHE_COMPRESSION="${env}", defaulting to no compression`) + return false +} +const cacheCompressionOption = parseCacheCompression(process.env.BUILD_CACHE_COMPRESSION)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
.github/copilot-instructions.md(1 hunks).github/workflows/pre-release-build.yml(1 hunks)build.mjs(12 hunks)package.json(3 hunks)
🧰 Additional context used
🪛 LanguageTool
.github/copilot-instructions.md
[grammar] ~33-~33: There might be a mistake here.
Context: ...opment CSS is injected via style-loader, in production CSS is extracted via MiniC...
(QB_NEW_EN)
🔇 Additional comments (12)
package.json (3)
78-79: Ensure esbuild is actually installed before the build runs.Adding esbuild and esbuild-loader to devDependencies is correct for the new minification pipeline.
96-97: LGTM! Build performance tooling additions.The addition of
style-loaderfor development builds andthread-loaderfor parallel processing aligns well with the performance optimization goals.
22-22: Keep @babel/runtime in dependencies
@babel/plugin-transform-runtimerewrites your code to import helpers (and regenerator runtime by default) from@babel/runtime, so it must remain independencies, notdevDependencies.Likely an incorrect or invalid review comment.
.github/workflows/pre-release-build.yml (2)
27-28: Node major version detection looks good.The approach for detecting Node major version is correct and will properly differentiate caches across Node versions.
37-45: Webpack cache configuration is well-structured.The cache paths and key structure appropriately include Node major version, which is important for native dependencies that might be compiled differently across Node versions.
.github/copilot-instructions.md (1)
22-31: Documentation of build performance options is comprehensive.The new build performance options are well-documented with clear defaults and use cases. The BUILD_PARALLEL and BUILD_THREAD options provide good control over resource usage.
build.mjs (6)
17-18: LGTM! Parallel build control implementation.The parallel build toggle with sensible default (on) provides good flexibility for different build environments.
82-82: Good fix for sass-embedded import issue.The destructuring of the default export properly addresses the previous review comment about sass-loader expecting the implementation object.
129-136: Excellent choice using esbuild for minification.Using esbuild for both JS and CSS minification should provide significant build performance improvements compared to Terser.
196-211: Thread-loader configuration is well-implemented.The conditional inclusion of thread-loader with appropriate pool timeout settings for different scenarios (production, watch-once, development) is well thought out.
345-354: Watch-once implementation is clean and correct.The implementation properly closes the watcher after the first build when BUILD_WATCH_ONCE is set.
417-427: Good solution for CSS 404 noise in development.Creating placeholder CSS files in development mode is a thoughtful solution to avoid console noise from 404 errors.
Also applies to: 437-446
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (6)
build.mjs (6)
17-17: Simplify boolean logic forparallelBuildThe ternary operator with explicit boolean values is unnecessarily verbose.
-const parallelBuild = process.env.BUILD_PARALLEL === '0' ? false : true +const parallelBuild = process.env.BUILD_PARALLEL !== '0'
35-42: Simplify CPU count fallback logicThe CPU count determination logic is overly complex and can be simplified using Math.max.
-let cpuCount = 1 -try { - const cpuInfo = os.cpus && os.cpus() - const len = Array.isArray(cpuInfo) ? cpuInfo.length : 0 - cpuCount = Number.isInteger(len) && len > 0 ? len : 1 -} catch { - cpuCount = 1 -} +const cpuCount = Math.max(1, os.cpus()?.length || 1)
130-130: Verify source map configuration change impactThe change from 'inline-source-map' to 'cheap-module-source-map' improves build performance but creates external .map files that need to be copied in development builds.
#!/bin/bash # Check if .map files are generated and if there's any handling for them echo "Checking for source map handling in development builds..." rg -n "\.map" build.mjs rg -n "source-map" build.mjs # Check if copyFiles function handles .map files ast-grep --pattern 'copyFiles($$$)'
506-558: Consider extracting duplicated webpack build logicThe production build contains significant code duplication between parallel and sequential builds. This pattern of wrapping runWebpack in promises is repeated multiple times.
Consider refactoring to reduce duplication:
+function createWebpackBuildPromise(isWithoutKatex, isWithoutTiktoken, minimal, tmpDir, suffix) { + return new Promise((resolve, reject) => + runWebpack(isWithoutKatex, isWithoutTiktoken, minimal, tmpDir, async (err, stats) => { + if (err || stats.hasErrors()) { + console.error(err || stats.toString()) + reject(err || new Error('webpack error')) + return + } + await finishOutput(suffix, tmpDir) + resolve() + }) + ) +} async function build() { await deleteOldDir() if (isProduction && !isAnalyzing) { const tmpFull = `${outdir}/.tmp-full` const tmpMin = `${outdir}/.tmp-min` + + const buildMin = () => createWebpackBuildPromise(true, true, true, tmpMin, '-without-katex-and-tiktoken') + const buildFull = () => createWebpackBuildPromise(false, false, false, tmpFull, '') + if (parallelBuild) { - await Promise.all([ - new Promise((resolve, reject) => - runWebpack(true, true, true, tmpMin, async (err, stats) => { - // ... duplicated code - }), - ), - new Promise((resolve, reject) => - runWebpack(false, false, false, tmpFull, async (err, stats) => { - // ... duplicated code - }), - ), - ]) + await Promise.all([buildMin(), buildFull()]) } else { - await new Promise((resolve, reject) => - runWebpack(true, true, true, tmpMin, async (err, stats) => { - // ... duplicated code - }), - ) - await new Promise((resolve, reject) => - runWebpack(false, false, false, tmpFull, async (err, stats) => { - // ... duplicated code - }), - ) + await buildMin() + await buildFull() } await fs.rm(tmpFull, { recursive: true, force: true }) await fs.rm(tmpMin, { recursive: true, force: true }) return } // ... rest of the function }
98-104: Consider using a more stable cache keyIncluding the full sourceBuildDir path in the cache key may cause unnecessary cache invalidation when build directories change between runs.
const variantId = [ isWithoutKatex ? 'no-katex' : 'with-katex', isWithoutTiktoken ? 'no-tiktoken' : 'with-tiktoken', minimal ? 'minimal' : 'full', - sourceBuildDir || outdir, + path.basename(sourceBuildDir || outdir), isProduction ? 'prod' : 'dev', ].join('|')
420-436: Critical: Build may silently skip missing artifactsThe copyFiles function now silently skips missing files when
fs.pathExistsreturns false, which could lead to incomplete builds being published without any failure signal. While CSS placeholders in dev mode are acceptable, critical JS files should never be skipped silently.Consider failing the build for missing critical files:
async function copyFiles(entryPoints, targetDir) { if (!fs.existsSync(targetDir)) await fs.mkdir(targetDir, { recursive: true }) await Promise.all( entryPoints.map(async (entryPoint) => { try { if (await fs.pathExists(entryPoint.src)) { await fs.copy(entryPoint.src, `${targetDir}/${entryPoint.dst}`) } else { - // Skip missing CSS in development (placeholders will be created later) const isCss = String(entryPoint.dst).endsWith('.css') - if (!isProduction || isCss) { - if (!isProduction && isCss) return + // Only skip CSS files in development (placeholders will be created) + if (!isProduction && isCss) { + console.log(`Skipping missing CSS in dev: ${entryPoint.src}`) + return } + // For all other files or production builds, this is a critical error throw new Error(`Missing build artifact: ${entryPoint.src}`) } } catch (e) { console.error('Copy failed:', entryPoint, e) throw e } }), ) }
🧹 Nitpick comments (1)
build.mjs (1)
401-415: Add error handling for directory creation in zipFolderThe zipFolder function should ensure the parent directory exists before creating the zip file.
The implementation correctly uses
fs.ensureDirto create parent directories, and the Promise-based error handling is comprehensive. Good implementation!
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
build.mjs(11 hunks)
🔇 Additional comments (6)
build.mjs (6)
89-96: LGTM! Sass implementation import fixed correctlyThe sass-embedded import now correctly uses the default export, which resolves the previous runtime error issue. The fallback to sass is also properly implemented.
364-399: LGTM! Proper compiler lifecycle managementThe compiler is now properly closed after production runs, preventing resource leaks. The watch mode also correctly handles the BUILD_WATCH_ONCE scenario with appropriate exit codes.
467-477: LGTM! Good solution for CSS 404 noise in developmentThe placeholder CSS files effectively prevent 404 errors in development mode without affecting production builds.
Also applies to: 487-497
149-156: LGTM! Effective esbuild integration for minificationThe migration from Terser to esbuild for both JS and CSS minification is well-implemented and should provide significant build performance improvements as reported in the PR objectives.
216-230: LGTM! Well-configured thread-loader integrationThe thread-loader configuration with dynamic worker allocation based on CPU cores and appropriate pool timeouts for different build scenarios is excellent.
255-255: LGTM! Smart CSS handling for dev vs productionUsing style-loader in development and MiniCssExtractPlugin in production is a best practice that speeds up development builds while maintaining proper CSS extraction for production.
Also applies to: 271-271, 286-286
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Codex Review: Here are some suggestions.
Reply with @codex fix comments to fix any unresolved comments.
About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you open a pull request for review, mark a draft as ready, or comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex fix this CI failure" or "@codex address that feedback".
ee5c711 to
4e0e3a7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (3)
build.mjs (3)
91-98: Stabilize cache key: avoid absolute paths in variantId to improve cache hits across environmentsUsing sourceBuildDir or outdir directly can include absolute paths, fragmenting cache keys between CI/local. Normalize to a relative path.
const variantId = [ isWithoutKatex ? 'no-katex' : 'with-katex', isWithoutTiktoken ? 'no-tiktoken' : 'with-tiktoken', minimal ? 'minimal' : 'full', - sourceBuildDir || outdir, + path.relative(process.cwd(), sourceBuildDir || outdir), isProduction ? 'prod' : 'dev', ].join('|')
376-389: Fix watch-once: await async callback before closing watcher/exitingWhen BUILD_WATCH_ONCE is set, the code exits before async work in the callback (e.g., finishOutput) completes, yielding partial outputs. Await the returned promise before closing the watcher and exiting.
- const watching = compiler.watch({}, (err, stats) => { + const watching = compiler.watch({}, (err, stats) => { const hasErrors = !!( err || (stats && typeof stats.hasErrors === 'function' && stats.hasErrors()) ) - callback(err, stats) - if (process.env.BUILD_WATCH_ONCE) { - watching.close((closeErr) => { - if (closeErr) console.error('Error closing watcher:', closeErr) - // Exit explicitly to prevent hanging processes in CI - // Use non-zero exit code when errors occurred - process.exit(hasErrors || closeErr ? 1 : 0) - }) - } + const ret = callback(err, stats) + const finalize = () => + watching.close((closeErr) => { + if (closeErr) console.error('Error closing watcher:', closeErr) + process.exit(hasErrors || closeErr ? 1 : 0) + }) + if (process.env.BUILD_WATCH_ONCE) { + if (ret && typeof ret.then === 'function') ret.then(finalize, finalize) + else finalize() + } })
123-124: Dev source maps: either copy .map files or revert to inline to avoid 404s in unpacked extensionWith devtool set to cheap-module-source-map, bundles reference external *.js.map files, but finishOutput doesn’t copy them, causing broken source maps in the browser. Choose one:
Option A — copy maps:
@@ - devtool: isProduction ? false : 'cheap-module-source-map', + devtool: isProduction ? false : 'cheap-module-source-map', @@ -async function finishOutput(outputDirSuffix, sourceBuildDir = outdir) { +async function finishOutput(outputDirSuffix, sourceBuildDir = outdir) { + const mapFiles = isProduction + ? [] + : [ + { src: `${sourceBuildDir}/shared.js.map`, dst: 'shared.js.map' }, + { src: `${sourceBuildDir}/content-script.js.map`, dst: 'content-script.js.map' }, + { src: `${sourceBuildDir}/background.js.map`, dst: 'background.js.map' }, + { src: `${sourceBuildDir}/popup.js.map`, dst: 'popup.js.map' }, + { src: `${sourceBuildDir}/IndependentPanel.js.map`, dst: 'IndependentPanel.js.map' }, + ] @@ - await copyFiles( - [...commonFiles, { src: 'src/manifest.json', dst: 'manifest.json' }], + await copyFiles( + [...commonFiles, ...mapFiles, { src: 'src/manifest.json', dst: 'manifest.json' }], chromiumOutputDir, ) @@ - await copyFiles( - [...commonFiles, { src: 'src/manifest.v2.json', dst: 'manifest.json' }], + await copyFiles( + [...commonFiles, ...mapFiles, { src: 'src/manifest.v2.json', dst: 'manifest.json' }], firefoxOutputDir, )Option B — revert to inline source maps in dev:
- devtool: isProduction ? false : 'cheap-module-source-map', + devtool: isProduction ? false : 'inline-source-map',Run to confirm maps are included after applying Option A:
#!/bin/bash rg -n "js\.map" -C2 build.mjs && echo "---" && rg -n "shared\.js\.map|content-script\.js\.map|background\.js\.map|popup\.js\.map|IndependentPanel\.js\.map" -C1Also applies to: 438-456, 460-463, 480-483
🧹 Nitpick comments (6)
build.mjs (3)
411-411: Prefer fs.ensureDir for idempotent directory creationSlightly simpler and race-safe.
- if (!fs.existsSync(targetDir)) await fs.mkdir(targetDir, { recursive: true }) + await fs.ensureDir(targetDir)
187-187: Document resolve.symlinks: false and its implicationsThis can affect npm/pnpm link and workspace symlinks. Add a note in docs (Build Performance or Bootstrap) about potential impact and workarounds.
499-571: Deduplicate build variant orchestration to reduce boilerplateThe Promise wrapping for the two production variants is repeated. Extract a small helper to run webpack and finishOutput to cut duplication and lower error surface.
Example:
function webpackRunPromise(args, suffix, dir) { return new Promise((resolve, reject) => runWebpack(...args, async (err, stats) => { if (err || stats.hasErrors()) { console.error(err || stats.toString()) reject(err || new Error('webpack error')) return } await finishOutput(suffix, dir) resolve() }), ) } // Usage: const buildMin = () => webpackRunPromise([true, true, true, tmpMin], '-without-katex-and-tiktoken', tmpMin) const buildFull = () => webpackRunPromise([false, false, false, tmpFull], '', tmpFull).github/copilot-instructions.md (3)
33-35: Tighten grammar/punctuation for clarityAdd commas around parenthetical phrases.
-Performance defaults: esbuild handles JS/CSS minification; in development CSS is injected via style-loader; -in production CSS is extracted via MiniCssExtractPlugin; thread-loader is enabled by default in both dev and prod. +Performance defaults: esbuild handles JS/CSS minification; in development, CSS is injected via style-loader; +in production, CSS is extracted via MiniCssExtractPlugin; thread-loader is enabled by default in both dev and prod.
20-32: Document symlink behavior (resolve.symlinks: false) and local-link caveatsAdd a short note so contributors using npm/pnpm link aren’t surprised.
Suggested addition:
- Note: Webpack is configured with resolve.symlinks: false to speed up/normalize module resolution. If you use npm link or pnpm workspaces with symlinked deps, local changes may not be picked up as expected. Workarounds: avoid symlinked deps for this repo, or temporarily enable symlink resolution in build.mjs while developing the linked package.
36-47: Mention dev source maps location/expectationsSince dev builds emit external source maps, note that .map files are included in build/* outputs to keep DevTools happy.
Suggested addition:
- Development: Bundles reference external source maps (.js.map). These files are copied into build/chromium/ and build/firefox*/ so DevTools can load them without 404s. If you don’t need source maps, set BUILD_SOURCEMAPS=0 (future option) or switch to inline maps locally.
Also applies to: 104-117
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
.github/copilot-instructions.md(1 hunks).github/workflows/pre-release-build.yml(1 hunks)build.mjs(11 hunks)package.json(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- package.json
- .github/workflows/pre-release-build.yml
🧰 Additional context used
🪛 LanguageTool
.github/copilot-instructions.md
[grammar] ~33-~33: There might be a mistake here.
Context: ...opment CSS is injected via style-loader; in production CSS is extracted via MiniC...
(QB_NEW_EN)
🔇 Additional comments (4)
build.mjs (4)
80-89: Correct sass-embedded default export usage — LGTMUsing the default export ensures sass-loader receives the implementation object; fallback to sass is correct.
35-55: Thread worker parsing/capping — LGTMPositive integer validation, CPU cap, and warnings look solid. Good defaulting to cpuCount.
56-59: Thread-loader timeout constant — LGTMNamed constant resolves the magic number. Pool timeout choices make sense (2s prod, 0 for watch-once, Infinity in dev).
Also applies to: 209-224
404-407: Remove .catch on archiver.finalize() (returns void) to avoid sync TypeError and broken zipsarchiver.finalize() does not return a promise; calling .catch throws synchronously and can break the zip step.
- archive.directory(dir, false) - archive.finalize().catch(onError) + archive.directory(dir, false) + try { + archive.finalize() + } catch (err) { + onError(err) + }Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (3)
build.mjs (3)
423-449: Avoid TOCTOU on copy; let fs.copy fail and handle ENOENT.Removes the race between pathExists and copy while keeping dev CSS skip behavior.
async function copyFiles(entryPoints, targetDir) { if (!fs.existsSync(targetDir)) await fs.mkdir(targetDir, { recursive: true }) await Promise.all( entryPoints.map(async (entryPoint) => { - try { - if (await fs.pathExists(entryPoint.src)) { - await fs.copy(entryPoint.src, `${targetDir}/${entryPoint.dst}`) - } else { - // Skip missing CSS in development (placeholders will be created later) - const isCss = String(entryPoint.dst).endsWith('.css') - if (!isProduction || isCss) { - if (!isProduction && isCss) { - console.log( - `[build] Skipping missing CSS file: ${entryPoint.src} -> ${entryPoint.dst} (placeholder will be created)`, - ) - return - } - } - throw new Error(`Missing build artifact: ${entryPoint.src}`) - } - } catch (e) { - console.error('Copy failed:', entryPoint, e) - throw e - } + try { + await fs.copy(entryPoint.src, `${targetDir}/${entryPoint.dst}`) + } catch (e) { + const isCss = String(entryPoint.dst).endsWith('.css') + if (e && e.code === 'ENOENT') { + if (!isProduction && isCss) { + console.log( + `[build] Skipping missing CSS file: ${entryPoint.src} -> ${entryPoint.dst} (placeholder will be created)`, + ) + return + } + console.error('Missing build artifact:', entryPoint.src) + } else { + console.error('Copy failed:', entryPoint, e) + } + throw e + } }), ) }
451-469: Dev source maps aren’t copied; DevTools shows 404s. Add .map copies in dev.Since devtool is
cheap-module-source-map, include emitted maps alongside bundles.async function finishOutput(outputDirSuffix, sourceBuildDir = outdir) { - const commonFiles = [ + const commonFiles = [ { src: 'src/logo.png', dst: 'logo.png' }, { src: 'src/rules.json', dst: 'rules.json' }, { src: `${sourceBuildDir}/shared.js`, dst: 'shared.js' }, { src: `${sourceBuildDir}/content-script.css`, dst: 'content-script.css' }, // shared { src: `${sourceBuildDir}/content-script.js`, dst: 'content-script.js' }, { src: `${sourceBuildDir}/background.js`, dst: 'background.js' }, { src: `${sourceBuildDir}/popup.js`, dst: 'popup.js' }, { src: `${sourceBuildDir}/popup.css`, dst: 'popup.css' }, { src: 'src/popup/index.html', dst: 'popup.html' }, { src: `${sourceBuildDir}/IndependentPanel.js`, dst: 'IndependentPanel.js' }, { src: 'src/pages/IndependentPanel/index.html', dst: 'IndependentPanel.html' }, - ] + // Dev-only: copy external source maps for CSP-safe debugging + ...(isProduction + ? [] + : [ + { src: `${sourceBuildDir}/content-script.js.map`, dst: 'content-script.js.map' }, + { src: `${sourceBuildDir}/background.js.map`, dst: 'background.js.map' }, + { src: `${sourceBuildDir}/popup.js.map`, dst: 'popup.js.map' }, + { src: `${sourceBuildDir}/IndependentPanel.js.map`, dst: 'IndependentPanel.js.map' }, + ]), + ]
370-404: Watch-once may exit before async finishOutput completes. Await the callback.Process can terminate early, leaving partial outputs. Chain the promise before closing watcher/exiting.
} else { - const watching = compiler.watch({}, (err, stats) => { + const watching = compiler.watch({}, (err, stats) => { const hasErrors = !!( err || (stats && typeof stats.hasErrors === 'function' && stats.hasErrors()) ) - callback(err, stats) + const ret = callback(err, stats) if (process.env.BUILD_WATCH_ONCE) { - watching.close((closeErr) => { - if (closeErr) console.error('Error closing watcher:', closeErr) - // Exit explicitly to prevent hanging processes in CI - // Use non-zero exit code when errors occurred - process.exit(hasErrors || closeErr ? 1 : 0) - }) + const finalize = () => + watching.close((closeErr) => { + if (closeErr) console.error('Error closing watcher:', closeErr) + process.exit(hasErrors || closeErr ? 1 : 0) + }) + if (ret && typeof ret.then === 'function') { + ret.then(finalize, finalize) + } else { + finalize() + } } }) }
🧹 Nitpick comments (5)
.github/copilot-instructions.md (3)
32-33: Fix list indentation to satisfy markdownlint (MD005/MD007).Align top-level and sub-bullets consistently.
- - BUILD_WATCH_ONCE (dev): When set, `npm run dev` runs a single build and exits (useful for timing) - - BUILD_POOL_TIMEOUT: Override thread-loader production pool timeout (ms) - - Default: `2000`. Increase if workers recycle too aggressively on slow machines/CI + - BUILD_WATCH_ONCE (dev): When set, `npm run dev` runs a single build and exits (useful for timing) +- BUILD_POOL_TIMEOUT: Override thread-loader production pool timeout (ms) + - Default: `2000`. Increase if workers recycle too aggressively on slow machines/CI
35-37: Tighten grammar and readability in “Performance defaults.”Capitalize clauses and use commas/periods for clarity.
-Performance defaults: esbuild handles JS/CSS minification; in development CSS is injected via style-loader; -in production CSS is extracted via MiniCssExtractPlugin; thread-loader is enabled by default in both dev and prod. +Performance defaults: esbuild handles JS/CSS minification. In development, CSS is injected via style-loader; +in production, CSS is extracted via MiniCssExtractPlugin. Thread-loader is enabled by default in both dev and prod.
20-37: Document source map behavior (external .map files in dev).Dev builds use external source maps; call out that .map files are written next to bundles to avoid DevTools 404s and that production builds omit them.
- BUILD_WATCH_ONCE (dev): When set, `npm run dev` runs a single build and exits (useful for timing) +- Source maps (dev): External `.map` files are emitted next to JS bundles for CSP‑safe debugging; production builds disable source maps.build.mjs (2)
136-151: Cache invalidation: include lockfiles/package.json in filesystem cache deps.Without these, cache may go stale when deps change.
cache: { type: 'filesystem', name: `webpack-${variantId}`, version: JSON.stringify({ THREAD: enableThread, WORKERS: threadWorkers, CACHE_COMP: cacheCompressionOption ?? false, PROD: isProduction, }), // default none; override via BUILD_CACHE_COMPRESSION=gzip|brotli compression: cacheCompressionOption ?? false, buildDependencies: { - config: [path.resolve('build.mjs')], + config: [ + path.resolve('build.mjs'), + path.resolve('package.json'), + path.resolve('package-lock.json'), + path.resolve('pnpm-lock.yaml'), + path.resolve('yarn.lock'), + ], }, },
199-205: Resolve.symlinks=false can surprise linked dev setups; document it.Add a short comment here and a note in docs about pnpm/npm link implications and why it’s set.
Would you like me to add a brief blurb under “Build Performance Options” explaining this choice?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
.github/copilot-instructions.md(1 hunks)build.mjs(11 hunks)
🧰 Additional context used
🪛 LanguageTool
.github/copilot-instructions.md
[grammar] ~35-~35: There might be a mistake here.
Context: ...opment CSS is injected via style-loader; in production CSS is extracted via MiniC...
(QB_NEW_EN)
🪛 markdownlint-cli2 (0.17.2)
.github/copilot-instructions.md
32-32: Inconsistent indentation for list items at the same level
Expected: 0; Actual: 1
(MD005, list-indent)
32-32: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
33-33: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
🔇 Additional comments (3)
build.mjs (3)
214-237: Thread-loader config looks solid.Good defaults, CPU-aware worker cap, and poolTimeout tuning for dev/watch-once.
56-67: Good: pool timeout is named and env-overridable.Clear default with validation; matches docs.
350-366: String-replace edits safe—no dangling syntax
Replacements only target standalone import/call lines insrc/background/index.mjs, producing valid comment lines without stray commas or bindings, and the fonts import insrc/content-script/styles.scssis present.
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Codex Review: Here are some suggestions.
Reply with @codex fix comments to fix any unresolved comments.
About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you open a pull request for review, mark a draft as ready, or comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex fix this CI failure" or "@codex address that feedback".
9ce694c to
9e5f428
Compare
|
@codex review |
|
Codex Review: Didn't find any major issues. Swish! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
/gemini review |
|
/gemini summary |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This is an excellent pull request that significantly improves the build process. The switch to esbuild, the addition of thread-loader, and the robust caching and parallelization strategies are all fantastic changes that will greatly enhance build performance and developer experience. The code is well-structured and handles many edge cases gracefully. I have a few minor suggestions to further improve code clarity and maintainability in the build script and its documentation, but overall, this is a very impressive and well-executed refactoring.
AGENTS.md
Outdated
| - BUILD_RESOLVE_SYMLINKS: When set to `1`/`true`, re-enable Webpack symlink resolution for `npm link`/pnpm workspace development (default off to avoid duplicate module instances) | ||
| - Source maps (dev): Dev builds emit external `.map` files next to JS bundles for CSP-safe debugging; production builds disable source maps | ||
| - Symlinks: Webpack uses `resolve.symlinks: false` to improve performance and ensure consistent module identity; use `BUILD_RESOLVE_SYMLINKS=1` when you need to work with linked dependencies locally |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation for BUILD_RESOLVE_SYMLINKS and Symlinks is a bit redundant as they describe the same feature. Consider merging them into a single point for better clarity and conciseness.
| - BUILD_RESOLVE_SYMLINKS: When set to `1`/`true`, re-enable Webpack symlink resolution for `npm link`/pnpm workspace development (default off to avoid duplicate module instances) | |
| - Source maps (dev): Dev builds emit external `.map` files next to JS bundles for CSP-safe debugging; production builds disable source maps | |
| - Symlinks: Webpack uses `resolve.symlinks: false` to improve performance and ensure consistent module identity; use `BUILD_RESOLVE_SYMLINKS=1` when you need to work with linked dependencies locally | |
| - Symlinks (`resolve.symlinks`): Default is `false` to improve performance and ensure consistent module identity. Set `BUILD_RESOLVE_SYMLINKS=1` to re-enable for `npm link`/pnpm workspace development. |
build.mjs
Outdated
| console.error('[build] Failed to load sass-embedded:', e1 && e1.message ? e1.message : e1) | ||
| console.error('[build] Failed to load sass:', e2 && e2.message ? e2.message : e2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error logging here could be improved for better debuggability. Instead of conditionally accessing the .message property, logging the entire error object (e1 and e2) would provide more context, including the stack trace, which is very helpful for diagnosing import failures.
| console.error('[build] Failed to load sass-embedded:', e1 && e1.message ? e1.message : e1) | |
| console.error('[build] Failed to load sass:', e2 && e2.message ? e2.message : e2) | |
| console.error('[build] Failed to load sass-embedded:', e1) | |
| console.error('[build] Failed to load sass:', e2) |
| if (parallelBuild) { | ||
| await Promise.all([ | ||
| createWebpackBuildPromise(true, true, true, tmpMin, '-without-katex-and-tiktoken'), | ||
| createWebpackBuildPromise(false, false, false, tmpFull, ''), | ||
| ]) | ||
| } else { | ||
| await createWebpackBuildPromise(true, true, true, tmpMin, '-without-katex-and-tiktoken') | ||
| await createWebpackBuildPromise(false, false, false, tmpFull, '') | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This block of code for handling parallel and sequential builds is a bit repetitive. You can refactor it to be more concise and maintainable by defining the build variants in an array and then iterating over them, which avoids duplicating the createWebpackBuildPromise calls.
const buildConfigs = [
[true, true, true, tmpMin, '-without-katex-and-tiktoken'],
[false, false, false, tmpFull, ''],
];
if (parallelBuild) {
await Promise.all(buildConfigs.map((args) => createWebpackBuildPromise(...args)));
} else {
for (const args of buildConfigs) {
await createWebpackBuildPromise(...args);
}
}
Summary of ChangesThis pull request significantly overhauls the build pipeline to boost performance, enhance configurability, and improve the developer experience. It achieves this by migrating to modern, faster tools like esbuild and sass-embedded, introducing intelligent parallelization and caching mechanisms, and refining build orchestration for both local development and CI environments. The changes aim to reduce build times, optimize resource utilization, and provide greater flexibility through environment variables. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Ignored Files
Activity
|
9e5f428 to
00b2a1f
Compare
|
@codex review |
|
/gemini review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
build.mjs (1)
191-199: The maintainer (PeterDaveHello) has already determined that license preservation concerns are out of scope for this browser extension project. While documenting the build policy rationale is technically sound practice, it is not required given the project's stated scope.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (4)
.github/workflows/pre-release-build.ymlAGENTS.mdbuild.mjspackage.json
🚧 Files skipped from review as they are similar to previous changes (2)
- package.json
- .github/workflows/pre-release-build.yml
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: PeterDaveHello
Repo: ChatGPTBox-dev/chatGPTBox PR: 886
File: build.mjs:165-171
Timestamp: 2025-09-14T18:01:17.933Z
Learning: In the ChatGPTBox project, the build.mjs webpack cache buildDependencies configuration should only track npm lockfiles (package-lock.json) and not include other package managers' lockfiles like pnpm-lock.yaml, yarn.lock, or bun.lockb, as confirmed by the maintainer PeterDaveHello.
📚 Learning: 2025-09-14T18:01:17.933Z
Learnt from: PeterDaveHello
Repo: ChatGPTBox-dev/chatGPTBox PR: 886
File: build.mjs:165-171
Timestamp: 2025-09-14T18:01:17.933Z
Learning: In the ChatGPTBox project, the build.mjs webpack cache buildDependencies configuration should only track npm lockfiles (package-lock.json) and not include other package managers' lockfiles like pnpm-lock.yaml, yarn.lock, or bun.lockb, as confirmed by the maintainer PeterDaveHello.
Applied to files:
build.mjs
📚 Learning: 2025-09-06T17:46:04.304Z
Learnt from: PeterDaveHello
Repo: ChatGPTBox-dev/chatGPTBox PR: 886
File: build.mjs:167-175
Timestamp: 2025-09-06T17:46:04.304Z
Learning: For the ChatGPTBox browser extension project, license preservation concerns about third-party dependencies may not be applicable according to the maintainer PeterDaveHello, who indicated this is the "wrong scope" for the project.
Applied to files:
build.mjs
🔇 Additional comments (10)
build.mjs (10)
17-91: Well-structured environment variable parsing.The environment helpers correctly handle various string representations and provide sensible defaults. The validation logic in
parseThreadWorkerCountensures worker counts are capped to available CPU cores, and the fallback handling is robust.
93-114: LGTM: Sass implementation loading with proper fallback.The caching pattern ensures the dynamic import only executes once per build process, and the fallback from
sass-embeddedtosasshandles environments where the preferred implementation isn't available. Error logging is helpful for debugging.
120-146: Good: Per-variant caching with Windows-safe separator.The variant identifier correctly incorporates all dimensions that affect build outputs (Katex, Tiktoken, minimal, directory, environment) and uses
__as a cross-platform safe separator. ThesourceBuildDirparameter enables proper per-variant output directory resolution.
147-340: Webpack configuration updates look solid.The per-variant output paths, filesystem caching, and CSS loader switching (style-loader in dev, extraction in prod) are well-implemented. The thread-loader integration properly adjusts pool timeouts for different build modes, and the Babel configuration enables caching for faster rebuilds.
413-464: Excellent error handling and lifecycle management.The production and watch paths correctly handle both synchronous and asynchronous callback errors. The
Promise.resolve().then()normalization ensures synchronous throws are caught, and thecallbackFailedtracking ensures exit codes reflect packaging failures, not just webpack compilation errors. The compiler close logic is properly sequenced.
466-505: Robust ZIP archive creation with proper stream handling.The implementation correctly handles both fatal errors and non-fatal warnings, uses the
closeevent for completion (ensuring file descriptors are flushed), and prevents double-settlement with thesettledflag. Thefinalize()call is synchronous and correctly starts the archive process with completion tracked via events.
507-530: Improved error handling with appropriate CSS placeholder logic.The copy logic correctly distinguishes between recoverable missing files (CSS in dev, where placeholders will be created) and critical missing artifacts (production or non-CSS). The error logging provides clear diagnostics, and the use of
typeofcheck before.endsWith()prevents undefined errors.
532-546: Clean solution for dev CSS placeholder generation.The function correctly creates placeholder CSS and sourcemap files only in development builds, preventing 404 errors when style-loader injects CSS at runtime instead of emitting files. The implementation uses proper existence checks and generates valid placeholder content.
548-611: Comprehensive output preparation with proper dev support.The updated
finishOutputcorrectly resolves artifacts fromsourceBuildDir, includes external source maps in dev builds for CSP-safe debugging, and dynamically derives the CSS placeholder list from the common files array. The use ofSetfor deduplication is appropriate.
613-695: Excellent build orchestration with comprehensive error handling.The refactored
build()function properly handles:
- Parallel vs. sequential production builds with
Promise.allSettled- Early setup failures (dynamic import rejections) via line 637 and 688
- Packaging failures that reject the build promise
- Dev watch mode that stays alive on compilation errors but fails on packaging errors
- Proper cleanup of temporary build directories in finally blocks
- Top-level fatal error handling with exit code 1
This addresses multiple critical past review concerns about hanging builds, silent failures, and improper exit codes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This is an excellent pull request that significantly improves the build pipeline's performance and reliability. The switch to esbuild for minification, the introduction of parallel builds, and the robust handling of async operations and process exits are all fantastic improvements. The code is well-structured and the new environment variables are well-documented. I have one suggestion to further improve the robustness of environment variable parsing.
| function getBooleanEnv(val, defaultValue) { | ||
| if (val == null) return defaultValue | ||
| const s = String(val).trim().toLowerCase() | ||
| if ( | ||
| s === '' || | ||
| s === '0' || | ||
| s === 'false' || | ||
| s === 'no' || | ||
| s === 'off' || | ||
| s === 'null' || | ||
| s === 'undefined' | ||
| ) { | ||
| return false | ||
| } | ||
| if (s === '1' || s === 'true' || s === 'yes' || s === 'on') { | ||
| return true | ||
| } | ||
| return defaultValue | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation of getBooleanEnv silently falls back to the default value for any unrecognized string. This could hide typos or misconfigurations (e.g., BUILD_PARALLEL=fals would be treated as true if that's the default). To improve robustness and provide better feedback to developers, I suggest adding a warning for unknown values, similar to how parseCacheCompressionOption and parseThreadWorkerCount handle them. This makes the behavior more predictable and helps catch configuration errors early. I've also removed null and undefined as explicit falsy string values, as they are more likely to indicate a configuration issue and should also trigger a warning.
function getBooleanEnv(val, defaultValue) {
if (val == null) return defaultValue
const s = String(val).trim().toLowerCase()
if (s === '' || s === '0' || s === 'false' || s === 'no' || s === 'off') {
return false
}
if (s === '1' || s === 'true' || s === 'yes' || s === 'on') {
return true
}
console.warn(`[build] Unknown boolean env value "${val}", defaulting to ${defaultValue}`)
return defaultValue
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 00b2a1f671
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "dependencies": { | ||
| "@babel/runtime": "^7.24.7", | ||
| "@mozilla/readability": "^0.6.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sync lockfile for new @babel/runtime dependency
package.json now declares @babel/runtime as a direct dependency, but the root dependency list in package-lock.json still omits it (see the top-level packages[""] block around lines 5‑39). With this mismatch, npm ci will error out because the lockfile is stale, preventing the build/lint steps from running in CI or locally.
Useful? React with 👍 / 👎.
00b2a1f to
65e8ca6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
build.mjs (1)
18-36: Consider warning on unrecognized boolean values.The function silently returns
defaultValuefor unrecognized strings (e.g.,'fals','maybe'), which could hide typos. Similar functions likeparseCacheCompressionOption(line 46) andparseThreadWorkerCount(line 70) warn on invalid input.Additionally, lines 27-28 check for the literal strings
'null'and'undefined', which seems overly defensive—users are unlikely to setBUILD_PARALLEL='null'intentionally.🔎 Suggested improvement
function getBooleanEnv(val, defaultValue) { if (val == null) return defaultValue const s = String(val).trim().toLowerCase() if ( s === '' || s === '0' || s === 'false' || s === 'no' || - s === 'off' || - s === 'null' || - s === 'undefined' + s === 'off' ) { return false } if (s === '1' || s === 'true' || s === 'yes' || s === 'on') { return true } + console.warn(`[build] Unknown boolean env value "${val}", defaulting to ${defaultValue}`) return defaultValue }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (4)
.github/workflows/pre-release-build.ymlAGENTS.mdbuild.mjspackage.json
🚧 Files skipped from review as they are similar to previous changes (2)
- .github/workflows/pre-release-build.yml
- package.json
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: PeterDaveHello
Repo: ChatGPTBox-dev/chatGPTBox PR: 886
File: build.mjs:165-171
Timestamp: 2025-09-14T18:01:17.933Z
Learning: In the ChatGPTBox project, the build.mjs webpack cache buildDependencies configuration should only track npm lockfiles (package-lock.json) and not include other package managers' lockfiles like pnpm-lock.yaml, yarn.lock, or bun.lockb, as confirmed by the maintainer PeterDaveHello.
📚 Learning: 2025-09-14T18:01:17.933Z
Learnt from: PeterDaveHello
Repo: ChatGPTBox-dev/chatGPTBox PR: 886
File: build.mjs:165-171
Timestamp: 2025-09-14T18:01:17.933Z
Learning: In the ChatGPTBox project, the build.mjs webpack cache buildDependencies configuration should only track npm lockfiles (package-lock.json) and not include other package managers' lockfiles like pnpm-lock.yaml, yarn.lock, or bun.lockb, as confirmed by the maintainer PeterDaveHello.
Applied to files:
build.mjs
📚 Learning: 2025-09-06T17:46:04.304Z
Learnt from: PeterDaveHello
Repo: ChatGPTBox-dev/chatGPTBox PR: 886
File: build.mjs:167-175
Timestamp: 2025-09-06T17:46:04.304Z
Learning: For the ChatGPTBox browser extension project, license preservation concerns about third-party dependencies may not be applicable according to the maintainer PeterDaveHello, who indicated this is the "wrong scope" for the project.
Applied to files:
build.mjs
🔇 Additional comments (6)
AGENTS.md (1)
20-38: Documentation is accurate and well-structured.The Build Performance Options section clearly documents all environment variables and their defaults, which align with the implementation in build.mjs. The documentation is helpful for developers configuring builds.
build.mjs (5)
93-114: LGTM! Sass implementation loading is well-designed.The caching strategy via
sassImplPromiseensures the dynamic import only happens once per process, and the fallback fromsass-embeddedtosasswith comprehensive error logging is robust.
413-463: LGTM! Callback error handling is robust.Both production and watch modes now correctly:
- Catch synchronous errors from callbacks (line 438)
- Handle async callback rejections (lines 430-434, 457-460)
- Propagate callback failures to exit codes (lines 424, 454)
This ensures build failures surface immediately rather than hanging or silently succeeding.
532-546: LGTM! Placeholder creation is now parameterized.The function now accepts
cssFilesas a parameter rather than hardcoding filenames, addressing previous maintainability concerns. CSS placeholders prevent 404 errors when using style-loader in development.
640-662: LGTM! Production build orchestration is robust.The parallel build path uses
Promise.allSettledto wait for all variants and reports the first failure, while the sequential path awaits each build individually. Thefinallyblock ensures temporary directories are cleaned up even when builds fail.
664-690: LGTM! Development build error handling balances resilience and fail-fast.Normal watch mode logs webpack errors but stays alive for auto-rebuild (line 672), while watch-once mode exits non-zero immediately (line 671). Packaging failures (finishOutput) always reject (line 680) to prevent silent success with incomplete artifacts.
|
@codex review |
|
Codex Review: Didn't find any major issues. Hooray! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Adopt esbuild minification, enable thread-loader by default, and support parallel production builds while keeping cache compression configurable. Consolidate boolean env parsing, cache the Sass implementation per process, and tune worker defaults for more predictable build behavior.
Propagate webpack build failures via explicit exit codes so production builds fail loudly. Derive dev CSS placeholders from the computed output list to avoid hardcoded filenames.
Document BUILD_RESOLVE_SYMLINKS and cache compression behavior, and tighten related parsing in the build script. Ensure watch-once packaging failures exit non-zero and simplify error logging. Key CI webpack cache by full Node version for safety.
65e8ca6 to
88bc3bc
Compare
|
@codex review |
|
/gemini review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces substantial build performance optimizations and configuration flexibility. Key changes include migrating from TerserPlugin to EsbuildPlugin for faster JavaScript minification, integrating thread-loader for parallel Babel processing, and implementing webpack's filesystem cache with variant-specific naming and configurable compression. The build process now supports parallel execution of production variants and offers new environment variables (BUILD_PARALLEL, BUILD_THREAD, BUILD_CACHE_COMPRESSION, BUILD_WATCH_ONCE, BUILD_POOL_TIMEOUT, BUILD_RESOLVE_SYMLINKS) for fine-grained control over build behavior. Additionally, the CSS loading strategy has been updated to use style-loader in development and MiniCssExtractPlugin in production, and Sass implementation is now dynamically resolved. The build.mjs file was refactored to include helper functions for environment variable parsing and robust error handling for webpack runs and file operations, including creating placeholder CSS and sourcemap files in development to prevent 404 errors. Dependency updates in package.json and package-lock.json reflect these changes, including the addition of new build tools and removal of several unused packages. A minor review comment suggested a stylistic change to use != null for null/undefined checks for consistency.
| } | ||
| function parseThreadWorkerCount(envValue, cpuCount) { | ||
| const maxWorkers = Math.max(1, cpuCount) | ||
| if (envValue !== undefined && envValue !== null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency with other helper functions in this file like getBooleanEnv and parseCacheCompressionOption, you can simplify this check. Using != null is a more concise way to check for both undefined and null.
| if (envValue !== undefined && envValue !== null) { | |
| if (envValue != null) { |
|
Codex Review: Didn't find any major issues. Can't wait for the next one! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 4 out of 5 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const maxWorkers = Math.max(1, cpuCount) | ||
| if (envValue !== undefined && envValue !== null) { | ||
| const rawStr = String(envValue).trim() | ||
| if (/^[1-9]\d*$/.test(rawStr)) { |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The regex pattern /^[1-9]\d*$/ will reject the value "0" as an invalid thread worker count. However, setting thread workers to 0 might be a valid scenario to disable threading entirely (which could align with BUILD_THREAD=0). Consider whether BUILD_THREAD_WORKERS=0 should be explicitly supported or if users should use BUILD_THREAD=0 instead. If the latter, the documentation should clarify that BUILD_THREAD_WORKERS must be >= 1.
| // Dev-only: copy external source maps for CSP-safe debugging | ||
| ...(isProduction | ||
| ? [] | ||
| : [ | ||
| { src: `${sourceBuildDir}/shared.js.map`, dst: 'shared.js.map' }, | ||
| { src: `${sourceBuildDir}/content-script.js.map`, dst: 'content-script.js.map' }, | ||
| { src: `${sourceBuildDir}/background.js.map`, dst: 'background.js.map' }, | ||
| { src: `${sourceBuildDir}/popup.js.map`, dst: 'popup.js.map' }, | ||
| { src: `${sourceBuildDir}/IndependentPanel.js.map`, dst: 'IndependentPanel.js.map' }, | ||
| ]), | ||
| ] | ||
|
|
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In development mode, source map files (.js.map) are attempted to be copied from sourceBuildDir but if they don't exist (for example, the first time the build runs or if webpack hasn't generated them yet), the copyFiles function will throw an error and fail the build. The copyFiles function only gracefully handles missing CSS files, not missing .map files. Consider extending the ENOENT handling to also skip missing .map files in development mode, similar to how CSS files are handled.
| // Dev-only: copy external source maps for CSP-safe debugging | |
| ...(isProduction | |
| ? [] | |
| : [ | |
| { src: `${sourceBuildDir}/shared.js.map`, dst: 'shared.js.map' }, | |
| { src: `${sourceBuildDir}/content-script.js.map`, dst: 'content-script.js.map' }, | |
| { src: `${sourceBuildDir}/background.js.map`, dst: 'background.js.map' }, | |
| { src: `${sourceBuildDir}/popup.js.map`, dst: 'popup.js.map' }, | |
| { src: `${sourceBuildDir}/IndependentPanel.js.map`, dst: 'IndependentPanel.js.map' }, | |
| ]), | |
| ] | |
| ] | |
| // Dev-only: conditionally copy external source maps for CSP-safe debugging | |
| if (!isProduction) { | |
| const devSourceMapFiles = [ | |
| { src: `${sourceBuildDir}/shared.js.map`, dst: 'shared.js.map' }, | |
| { src: `${sourceBuildDir}/content-script.js.map`, dst: 'content-script.js.map' }, | |
| { src: `${sourceBuildDir}/background.js.map`, dst: 'background.js.map' }, | |
| { src: `${sourceBuildDir}/popup.js.map`, dst: 'popup.js.map' }, | |
| { src: `${sourceBuildDir}/IndependentPanel.js.map`, dst: 'IndependentPanel.js.map' }, | |
| ] | |
| for (const file of devSourceMapFiles) { | |
| // In development, source maps may not yet exist (e.g., first build); skip missing ones. | |
| if (await fs.pathExists(file.src)) { | |
| commonFiles.push(file) | |
| } | |
| } | |
| } |
| }, | ||
| ) | ||
| // runWebpack is async; catch early rejections (e.g., failed dynamic imports) | ||
| if (ret && typeof ret.then === 'function') ret.catch(reject) |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing await. The value 'ret' is always a promise.
| } | ||
| }) | ||
| // Early setup failures (e.g., dynamic imports) should fail fast | ||
| if (ret && typeof ret.then === 'function') ret.catch(reject) |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing await. The value 'ret' is always a promise.
| return false | ||
| } | ||
| const cacheCompressionOption = parseCacheCompressionOption(process.env.BUILD_CACHE_COMPRESSION) | ||
| let cpuCount = 1 |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The initial value of cpuCount is unused, since it is always overwritten.
| let cpuCount = 1 | |
| let cpuCount |
User description
Replace JS/CSS minifiers with esbuild to reduce build times while preserving outputs.
Enable
thread-loaderby default in dev and production. Choose workers dynamically (based on CPU cores) and allow overrides via environment variables. Keep filesystem cache enabled and make cache compression configurable, defaulting to uncompressed for faster warm builds on CPU-bound machines. Add BUILD_PARALLEL toggle (default on) to switch between parallel and sequential production variant builds. Ensure watch-once dev builds exit cleanly.Adopt
sass-embeddedfor SASS processing. In development, usestyle-loaderto speed up CSS/SCSS compilation while keeping production outputs unchanged. Maintain CSP-safe source maps for extension contexts and suppress CSS 404 noise in development outputs.Additionally, dependency caching has been added to the GitHub Actions workflow to accelerate CI/CD runs.
Results on this DO VPS (2 cores, ~4 GiB RAM):
Times vary by environment; numbers above are for this machine.
Pull request summary from GitHub Copilot:
PR Type
Enhancement
Description
Replace Terser/CSS minifiers with esbuild for faster builds
Enable thread-loader by default with configurable parallelism
Add filesystem cache compression control and parallel build options
Switch to sass-embedded and style-loader for dev builds
Add GitHub Actions dependency caching for CI acceleration
Diagram Walkthrough
File Walkthrough
build.mjs
Major build system optimization with esbuild and threadingbuild.mjs
pre-release-build.yml
Add dependency and build caching to CI workflow.github/workflows/pre-release-build.yml
package.json
Update dependencies for build optimizationpackage.json
copilot-instructions.md
Document new build performance configuration options.github/copilot-instructions.md
Summary by CodeRabbit
Chores
Refactor
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.